package context

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
	"time"
)

func TestChannel() {
	var wg sync.WaitGroup
	var exitChan = make(chan struct{})
	wg.Add(1)

	go func() {
	LOOP:
		for {
			fmt.Println("work channel")
			time.Sleep(time.Second)

			select {
			case <-exitChan:
				break LOOP
			default:
			}
		}

		wg.Done()
	}()

	time.Sleep(3 * time.Second)
	close(exitChan)

	wg.Wait()
	fmt.Println("end")
}

func TestTimer() {
	t := time.NewTimer(3 * time.Second)
	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
	LOOP:
		for {
			fmt.Println("work timer")
			time.Sleep(time.Second)

			select {
			case <-t.C:
				break LOOP
			default:
			}
		}

		wg.Done()
	}()

	wg.Wait()
	fmt.Println("end")
}

func TestContext1() {
	var wg sync.WaitGroup
	ctx, cancel := context.WithCancel(context.Background())

	wg.Add(1)
	go func(ctx context.Context) {
	LOOP:
		for {
			fmt.Println("work context")
			time.Sleep(time.Second)

			select {
			case <-ctx.Done():
				break LOOP
			default:
			}
		}
		wg.Done()
	}(ctx)

	time.Sleep(3 * time.Second)
	cancel()

	wg.Wait()
	fmt.Println("end")
}

var wg sync.WaitGroup

func worker(ctx context.Context) {
	go worker2(ctx)
LOOP:
	for {
		fmt.Println("worker")
		time.Sleep(time.Second)
		select {
		case <-ctx.Done(): // 等待上级通知
			break LOOP
		default:
		}
	}
	wg.Done()
}

func worker2(ctx context.Context) {
LOOP:
	for {
		fmt.Println("worker2")
		time.Sleep(time.Second)
		select {
		case <-ctx.Done(): // 等待上级通知
			break LOOP
		default:
		}
	}
}

func TestContext2() {
	ctx, cancel := context.WithCancel(context.Background())
	wg.Add(1)
	go worker(ctx)

	time.Sleep(3 * time.Second)
	cancel()

	wg.Wait()
	fmt.Println("end")
}

func genChannel(ctx context.Context) chan int {
	channel := make(chan int)
	n := 1
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			case channel <- n:
				n++
				time.Sleep(time.Second)
			}
		}
	}()

	return channel
}
func TestCancel() {
	ctx, cancel := context.WithCancel(context.Background())
	channel := genChannel(ctx)

	for i := range channel {
		fmt.Println(i)
		if i == 5 {
			break
		}
	}

	cancel()
}

func TestTimeout() {
	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
	defer cancel()

	select {
	case <-ctx.Done():
		fmt.Println(ctx.Err())
	case <-time.After(1 * time.Second):
		fmt.Println("time out for 1 second")
	}
}

func GET(url string) (*http.Response, error) {
	request, _ := http.NewRequest("GET", url, nil)
	return http.DefaultClient.Do(request)
}

func Get(url string, timeout time.Duration) (string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	type RespData struct {
		resp *http.Response
		err  error
	}
	respChan := make(chan *RespData, 1)

	go func() {
		resp, err := GET(url)
		respData := RespData{
			resp, err,
		}
		respChan <- &respData
	}()

	select {
	case <-ctx.Done():
		return "", errors.New("timeout")
	case respData := <-respChan:
		resp := respData.resp
		err := respData.err
		if err != nil {
			return "", err
		}

		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return "", err
		}
		resp.Body.Close()
		return string(data), nil
	}
}
